%{
#include "stdio.h"
#include "symbol_table.h"

int getLineNumber(void);

%}

/* tokens for var types (leaf nodes)  */
%token KW_INT
%token KW_BYTE

/* tokens for control flow */
%token KW_IF
%token KW_THEN
%token KW_ELSE
%token KW_WHILE

/* tokens for commands */
%token KW_PRINT
%token KW_RETURN
%token KW_READ

/* tokens for operators */
%token OPERATOR_L
%token OPERATOR_G
%token OPERATOR_LE
%token OPERATOR_GE
%token OPERATOR_EQ
%token OPERATOR_NE
%token OPERATOR_AND
%token OPERATOR_OR

/* token for identifier */
%token <symbol> TK_IDENTIFIER

/* tokens for literals */
%token LIT_CHAR
%token LIT_INTEGER
%token LIT_STRING

%token TOKEN_ERROR   290

/* Intermediate nodes */


%union {
    table_entry_node* symbol;
}

%start program

%%

/* Production rule for the program */
program: declarations
    ;

declarations: var_declarations
    | func_declarations
    | func_declarations var_declarations declarations
    | var_declarations func_declarations declarations
    |
    ;

/* Production rule for declarations statements */
var_declarations: var_declaration
	| var_declarations var_declaration
	|
	;

/* Production rule for declaration statements */
var_declaration: type TK_IDENTIFIER ';'
    | type TK_IDENTIFIER '[' LIT_INTEGER ']' ';'
    ;

param: type TK_IDENTIFIER
	;

param_list: param
	| param_list param
	|
	;

/* Production rule for variable types */
type: KW_INT
	| KW_BYTE
	;

func_declarations: func_declaration
	| func_declarations func_declaration
	|
	;

func_declaration: type TK_IDENTIFIER '(' param_list ')' '{' statements '}'
	;

statements: simple_commands_sequence
    | simple_commands_sequence ';'
    | complex_commands_sequence
    | complex_commands_sequence ';'

    | simple_commands_sequence ';' statements
    | complex_commands_sequence ';' statements
    | complex_commands_sequence statements
    |
    ;

simple_commands_sequence: simple_command
    | simple_commands_sequence ';' simple_command
    ;

complex_commands_sequence: complex_command
    | complex_commands_sequence complex_command
    ;

command: simple_command
    | complex_command
    |
    ;

simple_command: print_command
    | read_command
    | return_command
    | assign_command
    ;

complex_command: commands_block
    | if_command
    | while_command
    ;

commands_block: '{' statements '}'
    ;

if_command: KW_IF '(' bool_expr ')' KW_THEN command
    | KW_IF '(' bool_expr ')' KW_THEN command KW_ELSE command
    ;

while_command: KW_WHILE '(' bool_expr ')' command
    ;

print_command: KW_PRINT printable_lits
    ;

printable_lits: LIT_STRING
	| expr
	;

read_command: KW_READ TK_IDENTIFIER
    ;

return_command: KW_RETURN expr
    ;

assign_command: l_value '=' expr
    | l_value '=' LIT_CHAR
    ;

l_value: TK_IDENTIFIER
    | TK_IDENTIFIER '[' expr ']'
    ;

bool_expr: expr OPERATOR_LE expr
    | expr OPERATOR_G expr
    | expr OPERATOR_L expr
    | expr OPERATOR_GE expr

    | expr OPERATOR_EQ expr
    | expr OPERATOR_NE expr
    | bool_expr OPERATOR_AND bool_expr
    | bool_expr OPERATOR_OR bool_expr
    | '(' bool_expr ')'
    ;

expr: expr '+' expr
    | expr '-' expr
    | expr '*' expr
    | expr '/' expr
    | '-' expr
    | '(' expr ')'
    | l_value
    | LIT_INTEGER
    ;

%%

int yyerror(char *s) {
    fprintf(stderr, "%s on line: %d\n", s, getLineNumber());

	// end the program with an error code.
	exit(3);
}